using System.Threading.Tasks;
using RPG.Domain.Character;
using RPG.Infrastructure.Events;
using RPG.Infrastructure.Persistence;
using RPG.Application.Commands.Character;
using RPG.Application.Queries.Character;

namespace RPG.Application.Services.Character
{
    public class CharacterService : ICharacterService
    {
        private readonly IRepository<Domain.Character.Character, CharacterId> _repository;
        private readonly IEventBus _eventBus;

        public CharacterService(
            IRepository<Domain.Character.Character, CharacterId> repository,
            IEventBus eventBus)
        {
            _repository = repository;
            _eventBus = eventBus;
        }

        public async Task<CharacterDTO> CreateCharacterAsync(CreateCharacterCommand command)
        {
            var character = Domain.Character.Character.Create(command.Name);
            
            await _repository.SaveAsync(character);
            
            foreach (var @event in character.DomainEvents)
            {
                await _eventBus.PublishAsync(@event);
            }
            
            return MapToDTO(character);
        }

        public async Task<CharacterDTO> GetCharacterAsync(CharacterId id)
        {
            var character = await _repository.GetByIdAsync(id);
            return MapToDTO(character);
        }

        public async Task<CharacterDTO> LevelUpCharacterAsync(LevelUpCharacterCommand command)
        {
            var character = await _repository.GetByIdAsync(command.CharacterId);
            if (character == null)
                throw new CharacterNotFoundException(command.CharacterId);

            character.LevelUp();
            
            await _repository.SaveAsync(character);
            
            foreach (var @event in character.DomainEvents)
            {
                await _eventBus.PublishAsync(@event);
            }
            
            return MapToDTO(character);
        }

        private static CharacterDTO MapToDTO(Domain.Character.Character character)
        {
            if (character == null)
                return null;

            return new CharacterDTO
            {
                Id = character.Id.Value,
                Name = character.Name,
                Level = character.Level.Value,
                Stats = new StatsDTO
                {
                    Strength = character.Stats.Strength,
                    Agility = character.Stats.Agility,
                    Intelligence = character.Stats.Intelligence,
                    Health = character.Stats.Health,
                    Mana = character.Stats.Mana
                },
                Equipment = new EquipmentDTO
                {
                    Weapon = MapToItemDTO(character.Equipment.Weapon),
                    Armor = MapToItemDTO(character.Equipment.Armor),
                    Accessory = MapToItemDTO(character.Equipment.Accessory)
                }
            };
        }

        private static ItemDTO MapToItemDTO(Domain.Items.Item item)
        {
            if (item == null)
                return null;

            return new ItemDTO
            {
                Id = item.Id.Value,
                Name = item.Name,
                Type = item.Type.ToString(),
                Stats = new StatsDTO
                {
                    Strength = item.Stats.Strength,
                    Agility = item.Stats.Agility,
                    Intelligence = item.Stats.Intelligence,
                    Health = item.Stats.Health,
                    Mana = item.Stats.Mana
                }
            };
        }
    }

    public class CharacterNotFoundException : System.Exception
    {
        public CharacterNotFoundException(CharacterId id)
            : base($"Character with ID {id.Value} not found.")
        {
        }
    }
}
